package service

import (
	"fmt"
	"github.com/Jeffail/tunny"
	mapset "github.com/deckarep/golang-set"
	"github.com/kordar/goutil"
	"github.com/kordar/k-means-server/src/pojo"
	"github.com/kordar/k-means-server/src/util"
	"log"
)

type OpticsReduce struct {
	Data        []pojo.FeatureItem
	MinPts      int                // 领域密度阈值
	Eps         float64            // 半径
	GroupNumber int64              // 分组数量
	RepeatArea  int                // 重叠数量
	visited     map[int]int        // 已标记，样本id, 簇id，-1为噪声
	orderList   []pojo.FeatureItem // 结果序列
	reachList   map[int]float64    // 可达距离
	Groups      map[pojo.FeatureItem][]pojo.FeatureItem
	ToMap       map[int]*pojo.FeatureItem // 样本所属分组map
}

func (reduce *OpticsReduce) Run() pojo.DbScanSegmentResult {

	pool := tunny.NewFunc(5, func(payload interface{}) interface{} {
		items := payload.([]pojo.FeatureItem)
		return reduce.sort(items)
	})
	defer pool.Close()

	mainResult := pojo.DbScanSegmentResult{
		Groups: make(map[pojo.FeatureItem]mapset.Set),
		ToMap:  make(map[int]*pojo.FeatureItem),
		Share:  make([]int, 0, reduce.RepeatArea),
	}

	results := make([]pojo.DbScanSegmentResult, 0)
	structGroupsOf := util.ArrayStructGroupsOf(reduce.Data, reduce.GroupNumber, reduce.RepeatArea)

	//count := 0
	for _, sub := range structGroupsOf {
		process := pool.Process(sub)
		if process != nil {
			result := process.(*pojo.DbScanSegmentResult)
			results = append(results, *result)
		}
	}

	for _, res := range results {
		if len(mainResult.Share) == 0 {
			mainResult = res
			continue
		}

		// 1、判断公共样本在当前样本中的位置
		for _, id := range mainResult.Share {
			if res.ToMap[id] != nil {
				targetG := res.ToMap[id] // 目标结点所在分组
				// 1.1 id在目标分段中存在分组，获取当前id在本分段中的位置
				g := mainResult.ToMap[id]
				if g != nil {
					set := mainResult.Groups[*g]
					if set == nil {
						continue
					}
					// id在本目标分段中存在分组，和并该分组到目标分组
					res.Groups[*targetG] = res.Groups[*targetG].Union(set)
					// 从现有分组中删除id所在分组
					delete(mainResult.Groups, *g)
				} else {
				}
			} else {
				// 双方结点都为nil，标识该样本为公共噪声
			}
		}

		// 将处理完的分段结点进行合并，即，将main 和并到target中
		// 3、合并toMap
		for k, sGroup := range res.ToMap {
			mainResult.ToMap[k] = sGroup
		}
		// 4、合并group
		for ss, items := range res.Groups {
			if mainResult.Groups[ss] == nil {
				mainResult.Groups[ss] = items
			} else {
				mainResult.Groups[ss] = mainResult.Groups[ss].Union(items)
			}
		}
		// 5、share为target的share，进行下一轮合并
		mainResult.Share = res.Share
	}

	/*
		for _, items := range mainResult.Groups {
			//log.Println("===========================================================")
			for c := range items.Iterator().C {
				//var item = c.(pojo.FeatureItem)
				mainResult.Notices.Contains(c)
				mainResult.Notices.Remove(c)
				//log.Println(item.Url, item.Id)
				count++
			}
		}
	*/

	return mainResult
}

func (reduce *OpticsReduce) sort(data []pojo.FeatureItem) *pojo.DbScanSegmentResult {
	body := make([]string, len(data))
	share := make([]int, reduce.RepeatArea)

	for i, datum := range data {
		body[i] = datum.Url
		share = append(share, datum.Id)
	}

	feature := pojo.FeatureResult{}
	if _, err := goutil.PostBodyHeader("http://product.yunpengai.com:17778/get-feature", body, nil, &feature); err != nil {
		log.Println(fmt.Sprintf("err = %v", err))
		return nil
	}

	if feature.Code != 0 {
		log.Println(feature.Message)
		return nil
	}

	// 循环特征值
	m := make(map[string][]float64, len(data))
	for _, info := range feature.Data {
		img := info.Img
		m[img] = util.StringToFloat64Array(info.Feature)
	}

	means := Optics{Data: data, FeatureMap: m, MinPts: reduce.MinPts, Eps: reduce.Eps}
	result := means.Run()

	groups := make(map[pojo.FeatureItem]mapset.Set)

	for item, items := range result {
		groups[item] = mapset.NewThreadUnsafeSet()
		for _, val := range items {
			groups[item].Add(val)
		}
	}

	segmentResult := pojo.DbScanSegmentResult{
		Groups: groups,
		ToMap:  means.ToMap,
		Share:  share,
	}

	return &segmentResult
}
